#!/usr/bin/python3
# -*- coding:utf-8 -*-
""""
@Author: 李吉凯
@Email: lijk34925@hundsun.com
@Time: 2023/3/7 10:41
@File: 指标规则任务执行.py
"""
import json
import re
import time
from time import sleep

import jsonpath

from BladeFerm.ApiModule.httpRequest import HttpRequest
from BladeFerm.Lib.ClassSelenium import SeleniumError
from BladeFerm.Lib.Common import data_processing, logger
from qturboframe.lib.utils.variable import Variable

# -------------- 被测系统的地址，登录信息 -----------
from DataTest.env import Env

system_login_address = 'http://10.20.46.217:8088'

# 调度系统的基础路由路径，反洗钱为空即可；内控需要更新为 '/scheduler/scheduler/1.0'
schedule_basic_url = ''

system_login_info = {
                        'operator_code': '378F8B35904CF45EE61FBAB59AE66DCA',
                        'password': '35F9954664545A6F'
                    }
# -------------- 环境变化后修改此处配置即可 ----------

"""
调度任务的运行脚本
    主要逻辑：
        1.登录，获取cookies
        2.判断任务是否在运行，先停止任务
        3.启动目标任务
        4.循环查询（最大时间300s）：
            5.判断任务是否执行完毕，执行完毕退出
"""

# -------------- http请求发送的客户端 初始化 --------------
httpClient = HttpRequest()
varb = Variable()

# -------------- 获取测试环境中配置的测试系统的地址 --------------
http_url = varb.get_variable('varg_system_ip')
if http_url:
    system_login_address = f"http://{http_url}:8088"
logger.info(f'http_url: {http_url}, system_login_address: {system_login_address}')

# -------------- 获取cookies和token信息并记录，用于判断登录是否执行 ----------
user_token = varb.get_variable('varc_user_token')
header_Set_Cookie = varb.get_variable('varc_header_Set_Cookie')
logger.info(f'varc_user_token: {user_token}, varc_header_Set_Cookie: {header_Set_Cookie}')
env = varb.get_variable('varg_db_type')


@data_processing
def indexJobAction(input_data=None, ui_expect=None):
    """
    执行调度任务并等待任务执行结束
    Args:
        input_data: 可选参数 max_times=300
        ui_expect: 可选参数 success=True

    Returns:

    """
    # 步骤一：登录系统，获取token
    env_name = input_data.pop("env", env)
    project = input_data.pop("project", 'AML5')
    updateParams(env=env_name, project=project)
    loginSystem(input_data=system_login_info)
    # 步骤二：查询任务启用状态，先启用任务
    max_times = input_data.get("max_times", 300)
    result = resumeTask(input_data)
    if result:
        # 步骤三：查询任务状态，先停止任务
        stopScheduleJob(input_data)
        # 步骤四：启动调度任务
        flag = actScheduleJob(input_data)
        index = 0
        except_success = bool(ui_expect.get('success', True)) if ui_expect else True
        success = False
        while flag and index < int(max_times):
            sleep(1)
            index += 1
            # 步骤四：查询调度任务执行结果
            result = queryScheduleJob(input_data)
            if result:
                success = True
                break
        if success == except_success:
            logger.info(f'调度任务执行结果符合期望结果{except_success}。')
            return True
        else:
            error = f'规定时间 {max_times} s内，调度任务执行结果不符合期望结果{except_success}'
            logger.error(error)
            raise SeleniumError(error)
    else:
        logger.info(f'执行动态子流程!')
        jobRedo(input_data)
        jobName = input_data.get('jobName')
        input_data.update({'jobNameAlias': '等级划分动态子任务', 'jobName': ''})
        # 查询动态子流程任务，1秒钟查一次,最多查询30次
        for i in range(max_times):
            result = queryScheduleJob(input_data)
            if result:
                logger.info(f'等级划分动态子任务 {jobName} 执行成功!')
                break
            time.sleep(1)
        return True


@data_processing
def httpRequest(input_data=None, ui_expect=None):
    """
    通用的http请求方法
    Args:
        input_data:
            url： 请求的路由
            type： 请求的类型，POST或GET
            headers： 请求头信息
            data: 请求参数
        ui_expect:

    Returns:

    """
    # 步骤一：登录系统，获取token
    env_name = input_data.pop("env", None)
    project = input_data.pop("project", 'AML5')
    if env_name:
        updateParams(env=env, project=project)
    loginSystem(input_data=system_login_info)
    # 步骤二：发送目标请求
    url_ext = input_data.get('url')
    url_typ = input_data.get('type', 'POST')
    headers = input_data.get('headers', {'Content-type': 'application/json'})
    data = input_data.get('data', {})
    url_start_job = system_login_address + url_ext
    if url_typ.upper() == 'POST':
        if re.search(r'application.json', str(headers), re.I):
            req_data = json.dumps(data)
        else:
            req_data = data
        params = None
    else:
        params = data
        req_data = None
    httpClient.send_request(url_typ, url_start_job,  params=params, data=req_data, headers=headers)
    res = httpClient.response
    exp_status = ui_expect.get('status', 200) if ui_expect else 200
    if res.status_code == int(exp_status):
        res_data = res.json()
        logger.info(f'请求断言成功，响应结果是：{res_data}')
        return True
    else:
        raise SeleniumError(f'请求响应断言失败：实际是:{res.status_code},期望是：{exp_status}')


def updateParams(env=None, project='AML5'):
    '''
    更新环境信息
    '''
    ENVObject = Env(env, project)
    global system_login_address
    global system_login_info
    global schedule_basic_url
    schedule_basic_url = '/scheduler/scheduler/1.0' if project == 'HG5' else ''
    system_login_address = f"http://{ENVObject.ip}:8088"
    operator_code = ENVObject.user
    password = ENVObject.pwd
    system_login_info = {
        'operator_code': operator_code,
        'password': password
    }
    logger.info(f'update url: {system_login_address}, params: {system_login_info}')


def loginSystem(input_data=None):
    """
    登录反洗钱5.0的系统
    Args:
        input_data:

    Returns:

    """
    logger.info(f"loginSystem cookies : {header_Set_Cookie}")
    if header_Set_Cookie and header_Set_Cookie != "AGENT_EMPTY":
        httpClient.headers_cookies = header_Set_Cookie
    else:
        url_start_job = system_login_address + '/g/hsxone.omc/v/submitLogin'
        header = {'Content-type': 'application/x-www-form-urlencoded'}
        data = {
                'operator_code': input_data.get('operator_code'),
                'password': input_data.get('password'),
                'is_change_login': False,
                'mac': 'OMC_LOGIN_WEB'
                }
        data.update(input_data)
        httpClient.send_request('POST', url_start_job, headers=header, data=data)
        res = httpClient.response
        if res.status_code == 200:
            token = res.json().get('user_token', None)
            httpClient.headers_cookies = f"token={token}"
            logger.info(f'cookies: {httpClient.headers_cookies}')
        else:
            raise SeleniumError(f'登录失败，无法获取token！')


def actScheduleJob(input_data: dict):
    """
    调度任务执行接口-triggernow.json
    Args:
        input_data: 调度接口的请求参数

    Returns:

    """
    url_start_job = system_login_address + schedule_basic_url + '/scheduler/timertask/triggernow.json'
    header = {'Content-type': 'application/x-www-form-urlencoded'}
    job_name = input_data.get('jobName')
    data = {'jobName': job_name,
            'jobGroup': input_data.get('jobGroup', 'default'),
            'taskParam': input_data.get('taskParam')}
    httpClient.send_request('POST', url_start_job, data=data, headers=header)
    res = httpClient.response
    res_data = res.json()
    logger.info(f'res: {res_data}')
    if res.status_code == 200 and res_data.get('success'):
        logger.info(f'启动任务 {job_name} 成功！')
        return True
    else:
        error_info = res_data.get('error')
        raise SeleniumError(f'任务[{job_name}]启动失败: {error_info}')


def queryScheduleJob(input_data: dict):
    """
    查询调度任务完成情况的接口-getAllTaskMonitor.json
    Args:
        input_data: 调度接口的请求参数
    Returns:

    """
    url_start_job = system_login_address + schedule_basic_url + '/scheduler/timertask/monitor/getAllTaskMonitor.json'
    # cookies = httpClient.cookies.get('token', None)
    header = {'Content-type': 'application/x-www-form-urlencoded',
              # 'Cookie': httpClient.headers_cookies
              }
    job_name = input_data.get('jobName')
    data = {
        'pageNo': 1,
        'pageSize': 20,
        'startTimeStr': None,
        'endTimeStr': None,
        'jobName': job_name,
        'jobNameAlias': input_data.get('jobNameAlias', None),
        'categoryId': None,
        'businessStartTime': None,
        'businessEndTime': None,
        'tag': None
    }
    httpClient.send_request('POST', url_start_job, data=data, headers=header)
    res = httpClient.response
    res_data = res.json()
    logger.debug(f'query job progress : {res_data}')
    if res.status_code == 200:
        if res_data.get('total') > 0:
            job_result = res_data.get('rows')[0]
            # 任务状态：execState：2 成功，5 失败，7 强制终止，12执行中 11等待中
            job_status = job_result.get('execState')
            if job_status == "2":
                return True
            elif job_status == "5":
                error_info = job_result.get('errorInfo')
                error_no = job_result.get('errorNo')
                raise SeleniumError(f'查询结果任务[{job_name}]执行失败!失败节点：{error_no}，失败信息：{error_info}')
            else:
                return False
        else:
            raise SeleniumError(f'查询任务[{job_name}]结果返回0！')
    else:
        error_info = res_data.get('error')
        raise SeleniumError(f'查询任务[{job_name}]结果失败: {error_info}')


def stopScheduleJob(input_data):
    """
    停止调度任务：先查询任务状态，再停止
    Args:
        input_data:

    Returns:

    """
    url_start_job = system_login_address + schedule_basic_url + '/scheduler/timertask/monitor/getAllTaskMonitor.json'
    # cookies = httpClient.cookies.get('token', None)
    header = {'Content-type': 'application/x-www-form-urlencoded',
              # 'Cookie': httpClient.headers_cookies
              }
    job_name = input_data.get('jobName')
    data = {
        'pageNo': 1,
        'pageSize': 20,
        'startTimeStr': None,
        'endTimeStr': None,
        'jobName': job_name,
        'jobNameAlias': input_data.get('jobNameAlias', None),
        'categoryId': None,
        'businessStartTime': None,
        'businessEndTime': None,
        'tag': None
    }
    httpClient.send_request('POST', url_start_job, data=data, headers=header)
    res = httpClient.response
    res_data = res.json()
    logger.info(f'res: {res_data}')
    if res.status_code == 200:
        if res_data.get('total') > 0:
            job_result = res_data.get('rows')[0]
            instance_id = job_result.get('instanceId')
            # 任务状态：execState：2 成功，5 失败，7 强制终止，12执行中 11等待中
            job_status = job_result.get('execState')
            if job_status not in ['2', '7']:
                url_start_job = system_login_address + schedule_basic_url + f'/scheduler/timertask/stopTask.json?instanceId={instance_id}'
                # cookies = httpClient.cookies.get('token', None)
                header = {'Content-type': 'application/x-www-form-urlencoded',
                          # 'Cookie': f'token={cookies}'
                          }
                job_name = input_data.get('jobName')
                httpClient.send_request('GET', url_start_job, data=None, headers=header)
                res = httpClient.response
                res_data = res.json()
                logger.info(f'res: {res_data}')
                if res.status_code == 200 and res_data.get('success'):
                    logger.info(f'停止任务[{job_name}]成功!')
                    return True
                else:
                    raise SeleniumError(f'停止任务[{job_name}]失败！')
        else:
            logger.info(f'jobname : {job_name}，未运行，已经是停止状态。')
    else:
        error_info = res_data.get('error')
        raise SeleniumError(f'查询任务[{job_name}]结果失败: {error_info}')


def resumeTask(input_data: dict):
    '''
    查询调度任务启用状态，如果未启用则启用
    '''
    url_query_job = system_login_address + schedule_basic_url + '/scheduler/timertask/getJobList.json'
    job_name = input_data.get('jobName')
    data = {
        'pageNo': 1,
        'pageSize': 20,
        'categoryId': 'systemdefault_timertasktype',
        'jobType': 'Process',
        'jobNameAlias': job_name
    }
    header = {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8',
              # 'Content-type': 'application/json',
              # 'Cookie': httpClient.headers_cookies
              }
    logger.info(f'查询调度任务启用状态请求参数{data}')
    httpClient.send_request('GET', url_query_job, params=data, headers=header)
    res = httpClient.response
    res_data = res.json()
    logger.info(f'查询调度任务启用状态{res_data}')
    if res_data['result']['total'] == 0:
        # raise Exception(f'环境{system_login_address}上调度任务：{job_name}未导入！')
        return False
    else:
        triggerState = jsonpath.jsonpath(res_data, f'$..jobEnable')
        logger.info(f'{job_name}启用状态为:{triggerState}')
        if not triggerState:
            raise SeleniumError(f"任务状态字段提取失败，请检查！")
        if "0" in triggerState:
            start_url = system_login_address + schedule_basic_url + '/scheduler/timertask/resumeTask.json'
            start_data = {'jobName': job_name, 'jobGroup': 'default'}
            header = {'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
            httpClient.send_request('GET', start_url, start_data, header)
            start_res = httpClient.response
            start_data = start_res.json()
            logger.info(f'start_data:{start_data}')
            if start_data.get('success') == True:
                logger.info(f"环境{system_login_address}上调度任务：{job_name}启动成功！")
            else:
                logger.info(f"环境{system_login_address}上调度任务：{job_name}启动失败！")
        return True


def jobRedo(input_data: dict):
    '''
    执行动态子流程
    '''
    job_name = input_data.get('jobName')
    url =system_login_address + schedule_basic_url + '/scheduler/timertask/jobRedo.json'
    # jobParams = '{"dynamic_subprocess_partition": {"name": "dynamic_subprocess_partition", "value": '+job_name+', "custom": true,"direct": "", "index": 0}}'
    data = {
        'instanceId': 'default20240320153820149641677',
        'jobParams': '{"dynamic_subprocess_partition": {"name": "dynamic_subprocess_partition", "value": "'+job_name+'", "custom": true,"direct": "", "index": 0}}'
    }
    header = {'Content-type': 'application/x-www-form-urlencoded',
              # 'Cookie':httpClient.cookies
              }
    httpClient.send_request('POST', url, data=data,headers=header)
    res = httpClient.response
    res_data = res.json()
    logger.info(f'查询调度任务启用状态{res_data}')


if __name__=='__main__':
    ruleNo = '400002001'
    # ruleNo = '600003001'
    env = 'mysql'
    indexJobAction(input_data={'jobName': ruleNo}, ui_expect={'flag': True}, env=env)



